package huriezy.quickcontacts.activities.fragments;

import huriezy.quickcontacts.R;
import huriezy.quickcontacts.activities.adapters.UserPickerAdapter;
import huriezy.quickcontacts.models.LogDebug;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.ProgressDialog;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.support.v4.app.Fragment;
import android.util.Base64;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;

import com.facebook.FacebookRequestError;
import com.facebook.HttpMethod;
import com.facebook.Request;
import com.facebook.Response;
import com.facebook.Session;
import com.facebook.SessionState;
import com.facebook.UiLifecycleHelper;
import com.facebook.model.GraphObject;

public class FacebookPickerFragment extends Fragment implements Session.StatusCallback {

	private final int			USER_SEARCH_LIMIT		= 25;

	public static final String	USER_TABLE				= "user";
	public static final String	UID_FIELD				= "uid";
	public static final String	NAME_FIELD				= "name";
	public static final String	FIRST_NAME_FIELD		= "first_name";
	public static final String	LAST_NAME_FIELD			= "last_name";
	public static final String	PICTURE_FIELD			= "pic_square";
	public static final String	ADDRESS_FIELD			= "current_address";
	public static final String	EMAIL_FIELD				= "email";
	public static final String	PICTURE_BITMAP_FIELD	= "picture_bitmap";

	private final String[]		fieldsToUse				= new String[] { UID_FIELD, NAME_FIELD, FIRST_NAME_FIELD,
			LAST_NAME_FIELD, PICTURE_FIELD, ADDRESS_FIELD, EMAIL_FIELD };

	public static final String	SEARCH_NAME_DATA		= "search_name_data";

	private ListView			userListView;
	private UserPickerAdapter	userAdapter;

	// Handler for facebook connection
	private UiLifecycleHelper	lifecycleHelper;

	// Handler for list adapter
	private final Handler		updateListHandler		= new Handler(Looper.getMainLooper());

	// Progress dialog
	private ProgressDialog progressDialog;
	private String contactName;
	
	// Listen to click on the list item and launch next fragment
	private OnItemClickListener	itemClickListener;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		lifecycleHelper = new UiLifecycleHelper(getActivity(), this);
		lifecycleHelper.onCreate(savedInstanceState);
	}
	
	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
		ViewGroup view = (ViewGroup) inflater.inflate(R.layout.fragment_user_picker, container, false);

		userListView = (ListView) view.findViewById(R.id.user_picker_list_view);
		if (itemClickListener != null) {
			userListView.setOnItemClickListener(itemClickListener);
		}

		ensureOpenSession(savedInstanceState);

		userAdapter = new UserPickerAdapter(getActivity());
		userListView.setAdapter(userAdapter);

		loadData();
		
		return view;
	}

	/**
	 * 
	 * @param savedInstanceState
	 */
	private void ensureOpenSession(Bundle savedInstanceState) {
		Session session = Session.getActiveSession();
		if (session == null) {
			if (savedInstanceState != null) {
				session = Session.restoreSession(getActivity(), null, this, savedInstanceState);
			}
			if (session == null) {
				session = new Session(getActivity());
			}
			Session.setActiveSession(session);
			if (session.getState().equals(SessionState.CREATED_TOKEN_LOADED)) {
				session.openForRead(new Session.OpenRequest(this).setCallback(this));
			}
		}
	}

	@Override
	public void call(Session session, SessionState state, Exception exception) {
		onSessionStateChanged(session, state, exception);
	}

	/**
	 * 
	 * @param session
	 * @param state
	 * @param exception
	 */
	private void onSessionStateChanged(Session session, SessionState state, Exception exception) {
		if (state.isOpened()) {
			LogDebug.info("Facebook session opened", getClass());
		}
		else if (state == SessionState.CLOSED_LOGIN_FAILED) {
			LogDebug.error("Cannot open facebook sassion, state CLOSED_LOGIN_FAILED", getClass());
			checkLogin();
		}
		else {
			LogDebug.warn("Facebook session status unhandled : " + state, getClass());
		}
	}

	/**
	 * Verify the keyhash generated for this application.
	 */
	public void checkLogin() {
		PackageInfo info;
		try {
			info = getActivity().getPackageManager().getPackageInfo("huriezy.quickcontacts",
					PackageManager.GET_SIGNATURES);

			for (Signature signature : info.signatures) {
				MessageDigest md = MessageDigest.getInstance("SHA");
				md.update(signature.toByteArray());
				LogDebug.info(Base64.encodeToString(md.digest(), Base64.DEFAULT), getClass());
			}
		}
		catch (NameNotFoundException e) {
			LogDebug.error("Error while trying to resolve the Facebook keyhash", getClass(), e);
		}
		catch (NoSuchAlgorithmException e) {
			LogDebug.error("Error while trying to resolve the Facebook keyhash", getClass(), e);
		}
	}
	
	@Override
	public void onResume() {
	    super.onResume();
	    lifecycleHelper.onResume();
	}

	@Override
	public void onActivityResult(int requestCode, int resultCode, Intent data) {
	    super.onActivityResult(requestCode, resultCode, data);
	    lifecycleHelper.onActivityResult(requestCode, resultCode, data);
	}

	@Override
	public void onPause() {
	    super.onPause();
	    lifecycleHelper.onPause();
	}

	@Override
	public void onDestroy() {
	    super.onDestroy();
	    lifecycleHelper.onDestroy();
	}

	@Override
	public void onSaveInstanceState(Bundle outState) {
	    super.onSaveInstanceState(outState);
	    lifecycleHelper.onSaveInstanceState(outState);
	}
	
	
	// /////////////////////// //
	// Data loading management //
	// /////////////////////// //	

	public void setContactName(String contactName) {
		this.contactName = contactName;
	}
	
	/**
	 * 
	 * @param contactName
	 */
	public void loadData() {
		LogDebug.info("Load data : " + contactName, getClass());
		
		progressDialog = ProgressDialog.show(getActivity(), "Please wait",
		          "Loading Facebook profiles...", true);
		searchUserFQLRequest(contactName);		
	}

	/**
	 * 
	 * @param contactName
	 */
	private void searchUserFQLRequest(String contactName) {
		if (contactName != null && contactName.length() > 0) {
			Bundle params = new Bundle();
			String query = generateSearchQuery(contactName, USER_SEARCH_LIMIT);
			params.putString("q", query);
			Session session = Session.getActiveSession();
			Request request = new Request(session, "/fql", params, HttpMethod.GET, new Request.Callback() {
				public void onCompleted(Response response) {
					parseFQLResponse(response);
				}
			});
			LogDebug.info("Execute FQL query '" + query + "'", getClass());
			Request.executeBatchAsync(request);
		}
	}

	/**
	 * 
	 * @param response
	 */
	private void parseFQLResponse(Response response) {
		LogDebug.info("Parse FQL result : " + response, getClass());
		FacebookRequestError error = response.getError();
		GraphObject graphObject = response.getGraphObject();
		if (error == null) {
			List<Bundle> listUsers = new ArrayList<Bundle>();
			if (graphObject != null) {
				JSONObject userJSONObject = graphObject.getInnerJSONObject();
				if (userJSONObject != null) {
					JSONArray userJsonArray = null;
					try {
						userJsonArray = userJSONObject.getJSONArray("data");
						LogDebug.info("User JSON Array : " + userJsonArray, getClass());
						if (userJsonArray != null) {
							for (int index = 0; index < userJsonArray.length(); index++) {
								Bundle user = generateUser(userJsonArray.getJSONObject(index));
								if (user != null) {
									listUsers.add(user);
								}
							}
						}
						else {
							LogDebug.warn("JSONArray is null", getActivity());
						}
					}
					catch (JSONException e) {
						LogDebug.error("JSON Parsing exception", getClass(), e);
					}
				}
			}
			else {
				LogDebug.warn("FQL response is null or empty : " + graphObject, getActivity());
			}
			if (!listUsers.isEmpty()) {
				initPictureBitmap(listUsers);
			}

		}
		else {
			LogDebug.error("Error detected in FQL response : " + error, getActivity());
		}
	}

	/**
	 * 
	 * @param listUsers
	 */
	private void initPictureBitmap(final List<Bundle> listUsers) {
		final Class<?> debugClassInfo = getClass();
		Thread imgUrlThread = new Thread(new Runnable() {
			@Override
			public void run() {
				for (Bundle user : listUsers) {
					String pictureUrlString = (String) user.get(FacebookPickerFragment.PICTURE_FIELD);
					URL pictureUrl = null;
					Bitmap pictureBitmap = null;
					try {
						pictureUrl = new URL(pictureUrlString);
						LogDebug.info("PictureURL : " + pictureUrl, debugClassInfo);
						if (pictureUrl != null) {
							pictureBitmap = BitmapFactory.decodeStream(pictureUrl.openConnection().getInputStream());
							LogDebug.info("Decode Stream", debugClassInfo);
							user.putParcelable(PICTURE_BITMAP_FIELD, pictureBitmap);
							LogDebug.info("Parcelable : " + user.getParcelable(PICTURE_BITMAP_FIELD), debugClassInfo);
						}
					}
					catch (MalformedURLException e) {
						LogDebug.error("MalformedURL", debugClassInfo, e);
					}
					catch (IOException e) {
						LogDebug.error("IOException", debugClassInfo, e);
					}
				}
				updateListHandler.post(new Runnable() {
					@Override
					public void run() {
						userAdapter.addAllUsers(listUsers);
						userAdapter.notifyDataSetChanged();
						if(progressDialog.isShowing()){
							progressDialog.dismiss();
						}
					}
				});
			}
		});
		imgUrlThread.start();

	}

	/**
	 * 
	 * @param contactName
	 * @param userSearchLimit
	 * @return
	 */
	private String generateSearchQuery(String contactName, int userSearchLimit) {
		StringBuilder builder = new StringBuilder();
		builder.append("SELECT ");
		boolean first = true;
		for (String fqlField : fieldsToUse) {
			if (!first) {
				builder.append(",");
			}
			first = false;
			builder.append(fqlField);
		}
		builder.append(" FROM ");
		builder.append(USER_TABLE);
		builder.append(" WHERE contains('");
		builder.append(contactName);
		builder.append("') LIMIT ");
		builder.append(USER_SEARCH_LIMIT);

		return builder.toString();
	}

	/**
	 * Generate a {@link Bundle} object from a donate {@link JSONArray} coming
	 * from a FQL query.
	 * 
	 * @param jsonObject
	 * @return
	 * @throws JSONException
	 */
	private Bundle generateUser(JSONObject jsonObject) throws JSONException {
		Bundle result = null;
		if (jsonObject != null) {
			result = new Bundle();

			String uid = jsonObject.getString(UID_FIELD);
			String name = jsonObject.getString(NAME_FIELD);
			String firstName = jsonObject.getString(FIRST_NAME_FIELD);
			String lastName = jsonObject.getString(LAST_NAME_FIELD);
			String pictureUrl = jsonObject.getString(PICTURE_FIELD);
			String email = jsonObject.getString(EMAIL_FIELD);
			String address = jsonObject.getString(ADDRESS_FIELD);

			LogDebug.info("Name : " + name, getClass());
			LogDebug.info("First Name : " + firstName, getClass());
			LogDebug.info("Last Name " + lastName, getClass());
			LogDebug.info("Picture Url " + pictureUrl, getClass());
			LogDebug.info("Email " + email, getClass());
			LogDebug.info("Address " + address, getClass());

			if (name == null) {
				if (firstName != null) {
					name = firstName;
				}
				if (lastName != null) {
					if (name != null) {
						name += " " + lastName;
					}
					else {
						name = lastName;
					}
				}
			}

			result.putString(UID_FIELD, uid);
			result.putString(NAME_FIELD, name);
			result.putString(EMAIL_FIELD, email);
			result.putString(ADDRESS_FIELD, address);
			result.putString(PICTURE_FIELD, pictureUrl);
		}
		return result;
	}

	public void setOnItemClickListener(OnItemClickListener listener) {
		itemClickListener = listener;
		if (userListView != null) {
			userListView.setOnItemClickListener(listener);
		}
	}
}
